#include <iostream>
#include <string>
#include <stdlib.h>
#include <cstring>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <cstdio>

using namespace std;

const int basesize = 1024;
const int gargvnum = 64;

//全局的命令行参数
char* gargv[gargvnum];
int gargc = 0;

//获取用户名
string GetUser(){
    string user = getenv("USER");
    return user.empty() ? "None" : user;
}
//获取主机名
string GetHostName(){
    string hostname = getenv("HOSTNAME");
    return hostname.empty() ? "None" : hostname;
}
//获取当前的工作路径
string GetPwd(){
    string pwd = getenv("PWD");
    return pwd.empty() ? "None" : pwd;
}
//1.打印命令行提示符
void PrintCommandLine(){
    //printf("[%s@%s:%s]# ", GetUser().c_str(), GetHostName().c_str(), GetPwd().c_str());

    char buffer[basesize];
    snprintf(buffer, basesize, "[%s@%s:%s]# ", GetUser().c_str(), GetHostName().c_str(), GetPwd().c_str());
    printf("%s", buffer);

    fflush(stdout);
}

//2.获取用户输入的命令行
bool GetCommandLine(char command_buffer[], int size){
    //用户输入的命令行是一个完整的字符串
    char* result = fgets(command_buffer, size, stdin);
    if(result == nullptr){
	return false;
    }
    command_buffer[strlen(command_buffer)-1] = '\0'; //清空用户输入的enter键，获取纯粹的命令
    if(strlen(command_buffer) == 0) return false; //如果获取的字符串为空，即用户只按了回车，就退回打印命令行提示符
    return true;
}

//3.分析命令行：把获取的命令行形成一张全局的命令行参数表
void AnalsysCommandLine(char command_buffer[], int len){
    (void)len;//传进来的len没有使用，防止报警告

    //在本次分析之前，需要把上次形成的命令行参数表清空(命令是一直循环进行的)
    memset(gargv, 0, gargvnum);
    gargc = 0;

    const char* sep = " ";
    gargv[gargc++] = strtok(command_buffer, sep);//第一次调用strtok，第一个参数直接传要切割的内容

    while(gargv[gargc++] = strtok(nullptr, sep));//这里是多次调用strtok，所以strtok的第一个参数传递nullptr，表示切割历史的最近一个字符串
    gargc--; //上一行碰到了nullptr，gargc还做了一次++,所以这里就--
}
void debug(){
    printf("argc:%d\n", gargc);
    for(int i = 0; gargv[i]; i++){
    	printf("argv[%d]:%s\n", i, gargv[i]);
    }
}

//4.执行命令:shell不能自己去执行，否则一旦遇到错误就崩了，所以需要创建子进程去执行
bool ExecuteCommand(){
    pid_t id = fork();
    if(id < 0) return false;
    else if(id == 0){
    	//子进程
	//1.执行命令
	execvp(gargv[0], gargv);
	//2.执行完就退出
	exit(1);
    }
    //父进程
    int status = 0;
    pid_t rid = waitpid(id, &status, 0);
    if(rid > 0){
    	//成功回收子进程
	return true;
    }
    return false;
}

int main(){
    char command_buffer[basesize];//存放获取到的用户输入的命令
    while(1){
    	PrintCommandLine(); //1.打印命令行提示符
	//sleep(50);
    
	//command_buffer是一个输出型参数
    	if( !GetCommandLine(command_buffer, basesize) )  //2.获取用户输入的命令行
	{
	    continue;//获取失败就重新获取
	}
	//printf("%s\n", command_buffer);

    	AnalsysCommandLine(command_buffer, strlen(command_buffer)); //3.分析命令行
	//debug();

    	ExecuteCommand();  //4.执行命令
    }
    return 0;
}
